package net.towee.client.com.repo.presenter;

import net.towee.client.com.repo.DiscussionShower;
import net.towee.client.event.MessageOperationEvent;
import net.towee.client.event.MessageOperationEvent.HasMessageOperationHandlers;
import net.towee.client.event.MessageOperationEvent.MessageOperationHandler;
import net.towee.client.event.core.DiscussionMessageReceivedEvent;
import net.towee.client.event.core.DiscussionMessageReceivedHandler;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.DiscussionManager;
import net.towee.model.account.AccountPermission;
import net.towee.model.discussion.Discussion;
import net.towee.model.discussion.DiscussionSubject;
import net.towee.model.discussion.Message;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

public class DiscussionShowerPresenter implements DiscussionShower, MessageOperationHandler, ClickHandler {

	public interface Display extends IsWidget {
		/**
		 * Show given message. If message with given id already showed
		 * it must replace it(maybe this message changed).
		 * 
		 * @param mess
		 * @param newMesage
		 */
		void showMessage(Message mess, boolean newMesage, boolean canEdit, boolean canRemove);
		
		/**
		 * Remove message from display
		 * @param messageId
		 */
		void removeMessage(String messageId);
		
		/**
		 * Return text with message for creating new message
		 * @return
		 */
		String getMessageText();
		
		/**
		 * Handler container for operation with messages
		 * @return
		 */
		HasMessageOperationHandlers getMessageHandlers();
		
		/**
		 * For getting more messages from the server.
		 * Must be hide when presenter invoke noMore method
		 * @return
		 */
		HasClickHandlers getMoreButton();
		
		// Events
		void onMessagePubleshed();
		void onMessageChanged();
		void onMessageRemoved();
		void onMessagesLoaded();
		void onMessagesLoading();
		
		/**
		 * Invoked when all messages loaded from the server
		 */
		void noMore();
	}

	// Injected
	private final Display display;
	private final DiscussionManager discManager;
	private final HandlerManager eventBus;
	private final AccountHelper account;
	
	// Discussion info
	private String id;
	private Discussion discussion;
	private DiscussionSubject subject; 
	
	// Permissions
	private boolean canEdit = false;
	private boolean canRemove = false;
	
	// Limits
	private int offset = 0;
	private final int limit = 30;
	
	@Inject
	public DiscussionShowerPresenter(Display display, DiscussionManager discManager,
			HandlerManager eventBus, AccountHelper account) {
		this.display = display;
		this.discManager = discManager;
		this.eventBus = eventBus;
		this.account = account;

		addHandlers();
	}

	private void addHandlers() {
		display.getMessageHandlers().addMessageOperationHandler(this);
		display.getMoreButton().addClickHandler(this);
		
		eventBus.addHandler(DiscussionMessageReceivedEvent.TYPE, new DiscussionMessageReceivedHandler() {
			@Override
			public void onDiscussionMessageReceived(Message mess, String discId, boolean local) {
				if(id.equals( discId ) && !local) 
					display.showMessage(mess, true, userCanEdit(mess), userCanRemove(mess));
			}
		});
	}
	
	private void setPermissions() {
		canEdit = account.hasPermissions(id, AccountPermission.CAN_CHANGE_OTHER_MESSAGES);
		canRemove = account.hasPermissions(id, AccountPermission.CAN_REMOVE_OTHER_MESSAGES);
	}
	
	private boolean userCanRemove(Message mess) {
		return canEdit | mess.getAuthor().equals( account.getAccount().getAccountIdentity() );
	}

	private boolean userCanEdit(Message mess) {
		return canRemove | mess.getAuthor().equals( account.getAccount().getAccountIdentity() );
	}

	private void changeMessage(Message message) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(display.getMessageText()), "null message text" );
		
		discManager.changeMessage(discussion, id, subject, message, new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				display.onMessageChanged();
			}
		});
	}

	private void removeMessage(Message message) {
		discManager.removeMessage(discussion, id, subject, message, new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				display.onMessageRemoved();
			}
		});
	}

	private void createMessage(Message message) {
		discManager.addMessage(discussion, id, subject, message, new AsyncCallback<Message>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Message mess) {
				display.showMessage(mess, true, userCanEdit(mess), userCanRemove(mess));
			}
		});
	}
	
	@Override
	public void onMessageOperation(MessageOperationEvent event) {
		switch(event.getEventType()) {
		case ADD:
			createMessage( event.getMessage() );
			break;
		case REMOVE:
			removeMessage( event.getMessage() );
			break;
		case CHANGE:
			changeMessage( event.getMessage() );
			break;
		}
	}

	@Override
	public void go(HasWidgets container) {
		Preconditions.checkNotNull(discussion, "null discussion container id");
		Preconditions.checkNotNull(subject, "null discussion subject");
		Preconditions.checkNotNull(id, "null discussion id");
		
		// Show container
		container.clear();
		container.add( display.asWidget() );
		
		// Set permission for fast processing
		// messages
		setPermissions();
		
		// Show discussion
		for(Message mess : discussion.getMessagesList())
			display.showMessage(mess, false, userCanEdit(mess), userCanRemove(mess));

		// Set offset
		offset = discussion.getMessagesList().size();
		
		// Set NoMore if needed
		maybeNoMore();
	}
	
	/**
	 * Maybe no more messages for requesting from the server 
	 */
	private void maybeNoMore() {
		if(offset == discussion.getCount())
			display.noMore();
	}
	
	/**
	 * Get more messages from server
	 */
	private void getMessages() {
		display.onMessagesLoading();
		discManager.getDiscussion(id, subject, new AsyncCallback<Discussion>(){
			@Override
			public void onFailure(Throwable caught) {	
			}

			@Override
			public void onSuccess(Discussion discussion) {
				display.onMessagesLoaded();
				
				// Set new offset
				offset += limit;
				
				// Show messages
				for(Message mess : discussion.getMessagesList())
					display.showMessage(mess, false, userCanEdit(mess), userCanRemove(mess));
				
				// Maybe no more messages?
				maybeNoMore();
			}
		});
	}

	@Override
	public void setSubject(DiscussionSubject subject) {
		this.subject = subject;
	}

	@Override
	public void setDiscussion(Discussion disc) {
		this.discussion = disc;
	}

	@Override
	public void setContainerId(String id) {
		this.id = id;
	}

	@Override
	public void onClick(ClickEvent event) {
		getMessages();
	}
}
